from abc import ABC
from copy import copy
from collections import defaultdict
from datetime import datetime

from vn_local.trader.constant import Interval, Direction, OrderType, Exchange, Product, ApiSource
from vn_local.trader.object import (BarData, TickData, ContractData, OrderData, TradeData,
                                    OrderTradeData, PublicTradeData, AccountData, Status, QueryOpenOrderRequest,
                                    seconds_over_order_create, seconds_over_order_cancelling_or_querying)
from vn_local.trader.utility import virtual, round_to, load_json, extract_vt_symbol
from .base import StopOrder, EngineType
from typing import Dict, Optional, Any, Callable, List
import time
import os
import signal


def select_orders_for_query_and_cancel(orders, max_nums):
    """
    选择查询次数最少的订单，并返回订单ID的列表。

    :param orders: 一个包含订单ID和查询次数的列表，形如 [{'id': 1, 'queries': 2}, ...]
    :param max_nums: 每次最多查询的订单数量
    :return: 返回一个订单ID的列表
    """
    # 按查询次数从少到多排序
    sorted_orders = sorted(orders, key=lambda x: x['nums'])

    # 选择前 max_queries 个订单的ID
    selected_order_ids = [order['id'] for order in sorted_orders[:max_nums]]

    return selected_order_ids


class CtaTemplate(ABC):
    """"""
    setting_filename: str = "mm_strategy_setting.json"
    data_filename: str = "mm_strategy_data.json"

    author: str = ""
    parameters: list = []
    variables: list = []

    def __init__(
            self,
            cta_engine: Any,
            strategy_name: str,
            vt_symbol: str,
            setting: dict,

    ) -> None:
        """"""

        self.cta_engine: Any = cta_engine
        self.strategy_name: str = strategy_name
        self.vt_symbol: str = vt_symbol

        self.inited: bool = False
        self.trading: bool = False
        self.pos: int = 0

        self.count: int = 0
        self.wait: bool = False
        self.wait_triggered_time = 0

        self.last_timer = time.time()
        # Copy a new variables list here to avoid duplicate insert when multiple
        # strategy instances are created with the same strategy class.
        self.variables = copy(self.variables)
        self.variables.insert(0, "inited")
        self.variables.insert(1, "trading")
        self.variables.insert(2, "pos")

        # 订单管理
        self.strategy_order_trades: List[str] = []
        self.order_trades: Dict[str, OrderTradeData] = {}

        self.sell_orderid_order_dict = {}
        self.buy_orderid_order_dict = {}

        # 成交管理
        self.orderid_traded_map: Dict[str, float] = defaultdict(int)  # orderid volume traded map
        self.symbol_traded: Dict[Direction, float] = defaultdict(int)
        self.symbol_traded_cost: Dict[Direction, float] = defaultdict(int)

        self.check_order_status_count = 0
        self.check_order_status_interval = 10

        self.max_batch_query_order_nums = 10
        self.max_batch_cancel_order_nums = 10

        self.max_query_nums = 1
        self.max_cancel_nums = 1

        self.querying_open_orders = False
        self.open_orders: Dict[str, OrderTradeData] = {}
        self.to_recover_queue = set()
        # self.max_cancel_nums = 10

        self.update_setting(setting)

    def update_setting(self, setting: dict) -> None:
        """
        Update strategy parameter wtih value in setting dict.
        """
        for name in self.parameters:
            if name in setting:
                setattr(self, name, setting[name])
        self.on_update_parameters(setting)

    @classmethod
    def get_class_parameters(cls) -> dict:
        """
        Get default parameters dict of strategy class.
        """
        class_parameters: dict = {}
        for name in cls.parameters:
            class_parameters[name] = getattr(cls, name)
        return class_parameters

    def get_parameters(self) -> dict:
        """
        Get strategy parameters dict.
        """
        strategy_parameters: dict = {}
        for name in self.parameters:
            strategy_parameters[name] = getattr(self, name)
        return strategy_parameters

    def get_current_parameters(self) -> dict:
        strategy_setting = load_json(self.setting_filename)

        for strategy_name, strategy_config in self.strategy_setting.items():
            self.add_strategy(
                strategy_config["class_name"],
                strategy_name,
                strategy_config["vt_symbol"],
                strategy_config["setting"]
            )

    def get_variables(self) -> dict:
        """
        Get strategy variables dict.
        """
        strategy_variables: dict = {}
        for name in self.variables:
            strategy_variables[name] = getattr(self, name)
        return strategy_variables

    def get_data(self) -> dict:
        """
        Get strategy data.
        """
        strategy_data: dict = {
            "strategy_name": self.strategy_name,
            "vt_symbol": self.vt_symbol,
            "class_name": self.__class__.__name__,
            "author": self.author,
            "parameters": self.get_parameters(),
            "variables": self.get_variables(),
        }
        return strategy_data

    @virtual
    def on_init(self) -> None:
        """
        Callback when strategy is inited.
        """
        pass

    @virtual
    def on_start(self) -> None:
        """
        Callback when strategy is started.
        """
        pass

    @virtual
    def on_stop(self) -> None:
        """
        Callback when strategy is stopped.
        """
        pass

    @virtual
    def on_tick(self, tick: TickData) -> None:
        """
        Callback of new tick data update.
        """
        pass

    @virtual
    def on_public_trade(self, public_trade: PublicTradeData) -> None:
        """
        Callback of new public trade data update
        """
        pass

    @virtual
    def on_order_trade(self, order_trade: OrderTradeData) -> None:
        pass

    @virtual
    def on_bar(self, bar: BarData) -> None:
        """
        Callback of new bar data update.
        """
        pass

    @virtual
    def on_interval(self) -> None:
        pass

    @virtual
    def on_trade(self, trade: TradeData) -> None:
        """
        Callback of new trade data update.
        """
        pass

    @virtual
    def on_update_parameters(self, setting) -> None:
        pass

    @virtual
    def on_order(self, order: OrderData) -> None:
        """
        Callback of new order data update.
        """
        pass

    @virtual
    def on_stop_order(self, stop_order: StopOrder) -> None:
        """
        Callback of stop order update.
        """
        pass

    @virtual
    def on_account(self, account: AccountData):
        pass

    def on_strategy_data(self, data: dict) -> None:
        pass

    def add_to_recovery_queue(self, recover_queue_content: Dict):
        msg = f"[on_add_to_recovery_queue]"
        send_content = {}
        for vt_orderid in list(recover_queue_content.keys()):
            if vt_orderid not in self.to_recover_queue:
                self.to_recover_queue.add(vt_orderid)
                send_content[vt_orderid] = recover_queue_content[vt_orderid]

        if send_content:
            msg = f"{msg} {send_content}"
            self.write_log(msg)
            self.send_dingding(msg)

    def recover_orders(self):
        to_recover = {}
        for vt_orderid in self.to_recover_queue:
            order_trade = self.order_trades.get(vt_orderid, None)
            if not order_trade:
                msg = f"[on_recover_orders] :{vt_orderid}未发出过,请检查"
                self.write_log(msg)
                self.send_dingding(msg)
                continue

            # 状态已经回来的订单
            elif order_trade.status == Status.PARTTRADED:
                msg = (f"[on_recover_orders] :{vt_orderid} {Status.CANCELLING} -> {Status.PARTTRADED}"
                       f", remove from recovery queue ")
                self.write_log(msg)
                self.send_dingding(msg)
                to_recover[vt_orderid] = (Status.CANCELLING, Status.PARTTRADED)

            elif order_trade.status == Status.CANCELLING:
                if vt_orderid in self.open_orders:
                    "撤单未成功 -> failed"
                    to_recover[vt_orderid] = (Status.CANCELLING, Status.FAILED)

                else:
                    "撤单已成功 -> cancelled"
                    to_recover[vt_orderid] = (Status.CANCELLING, Status.CANCELLED)

            elif order_trade.status == Status.PENDING:
                if vt_orderid in self.open_orders:
                    "下单已成功 -> new"
                    to_recover[vt_orderid] = (Status.PENDING, Status.NOTTRADED)

                else:
                    "下单未成功 -> rejected"
                    to_recover[vt_orderid] = (Status.PENDING, Status.REJECTED)

            elif order_trade.status == Status.UNKNOWN:
                if vt_orderid in self.open_orders:
                    "下单成功/撤单失败"
                    if order_trade.cancel_num == 0:
                        open_order_trade = self.open_orders[vt_orderid]
                        to_recover[vt_orderid] = (Status.UNKNOWN, open_order_trade.status)
                    else:
                        to_recover[vt_orderid] = (Status.UNKNOWN, Status.FAILED)

                else:
                    "下单失败/撤单成功"
                    if order_trade.cancel_num == 0:
                        to_recover[vt_orderid] = (Status.UNKNOWN, Status.REJECTED)
                    else:
                        to_recover[vt_orderid] = (Status.UNKNOWN, Status.CANCELLED)

        for vt_orderid, (previous_status, expected_status) in to_recover.items():
            self.push_strategy_order_trade(vt_orderid, expected_status=expected_status)
            msg = (f"[on_recover_orders] :{vt_orderid} {previous_status.value} -> {expected_status}"
                   f" , remove from recovery queue ")
            self.write_log(msg)
            # self.send_dingding(msg)
            if vt_orderid in self.to_recover_queue:
                self.to_recover_queue.remove(vt_orderid)

    def update_order_trade(self, order_trade: OrderTradeData):

        volume_traded = self.orderid_traded_map.get(order_trade.vt_orderid, 0)
        trade_diff = order_trade.traded - volume_traded

        if trade_diff > 0:
            self.orderid_traded_map[order_trade.vt_orderid] = order_trade.traded
            order_trade.trade_diff = order_trade.traded - volume_traded
        else:
            order_trade.trade_diff = 0

        if order_trade.traded < order_trade.volume and order_trade.status == Status.ALLTRADED:
            order_trade.status = Status.PARTTRADED
            if order_trade.type == OrderType.FAK:
                order_trade.status = Status.CANCELLED

        if order_trade.api_source == ApiSource.QUERYOPEN:
            # self.write_log(f"[update_order_trade:queryopen] {order_trade}")
            self.on_query_open_order(order_trade)

        elif order_trade.api_source == ApiSource.QUERY:
            """
            单独处理查询回来的订单
            """
            self.write_log(f"[on_query_order_response] {order_trade.vt_orderid}: {order_trade}")
            self.on_order_trade(order_trade)

        else:
            self.on_order_trade(order_trade)

    def on_query_open_order(self, order_trade: OrderTradeData):

        if order_trade.vt_orderid not in self.open_orders:
            self.open_orders[order_trade.vt_orderid] = order_trade
            self.write_log(
                f"[on_query_open_orders: processing]:添加第{len(self.open_orders)}张 {order_trade.vt_orderid},"
                f" 一共{order_trade.error_id}张")

        if len(self.open_orders) == order_trade.error_id:
            """
            open orders 众神归位,开始修正订单状态
            """
            self.recover_orders()
            if self.to_recover_queue:
                # 还有没恢复的订单, 发消息再清除
                msg = f"[on_query_open_order: error on finished] {self.to_recover_queue} 还未恢复,请增加case"
                self.write_log(msg)
                self.send_dingding(msg)
                self.to_recover_queue = set()

            msg = f"[on_query_open_order: finished] {list(self.open_orders.keys())}]"
            self.querying_open_orders = False
            self.write_log(msg)
            self.open_orders: Dict[str, OrderTradeData] = {}

    def update_timer(self):
        """"""
        self.on_interval()

    def is_submitting_too_long(self, vt_orderid, response_tolerant=30):
        if not seconds_over_order_create(vt_orderid) > response_tolerant:
            return False

        return True

    def get_order_trade_from_oms_engine(self, vt_orderid):
        oms_engine = self.cta_engine.main_engine.get_engine("oms")
        order_trade = oms_engine.get_order_trade(vt_orderid)
        return order_trade

    def push_strategy_order_trade(self, vt_orderid, expected_status: Status, expected_error_msg=""):
        order_trade = self.order_trades.get(vt_orderid, None)
        order_trade.status = expected_status
        order_trade.api_source = ApiSource.RECOVERY
        order_trade.error_msg = expected_error_msg
        self.write_log(f"[on_push_strategy_order_trade] push order trade {order_trade}")
        self.on_order_trade(order_trade)

    def check_order_status(self):
        query_orders = []
        # cancel_orders = []

        recover_queue_content = {}

        for vt_orderid in self.strategy_order_trades:
            order_trade = self.order_trades.get(vt_orderid, None)

            # case0 submitting too long
            if not order_trade:
                """

                """
                if self.is_submitting_too_long(vt_orderid):
                    self.write_log(f"[on_check_order_status] {vt_orderid} not found, try to get from oms engine")
                    order_trade = self.get_order_trade_from_oms_engine(vt_orderid)
                    if not order_trade:
                        msg = f"[on_check_order_status] get from oms engine failed, {vt_orderid} please check"
                        self.write_log(msg)
                        self.send_dingding(msg)
                        return

                    self.push_strategy_order_trade(vt_orderid, Status.REJECTED, expected_error_msg="SubmittingTooLong")

                continue

            # case1 cancel failed
            # if order_trade.is_cancel_failed():
            #     if order_trade.cancel_num < self.max_cancel_nums:
            #         cancel_order = {"id": vt_orderid, "nums": order_trade.cancel_num}
            #         cancel_orders.append(cancel_order)
            #     else:
            #         recover_queue_content[vt_orderid] = "cancel failed"
            # self.add_to_recovery_queue(vt_orderid, "cancel failed")

            # case2 cancelling
            if order_trade.is_cancelling_too_long():
                # if order_trade.cancel_num < self.max_query_nums:
                if order_trade.query_num < self.max_query_nums:
                    # query_order = {"id": vt_orderid, "nums": order_trade.cancel_num}
                    query_order = {"id": vt_orderid, "nums": order_trade.query_num}
                    query_orders.append(query_order)
                else:
                    recover_queue_content[vt_orderid] = "cancelling"
                    # self.add_to_recovery_queue(vt_orderid, "cancelling")
                continue

            # case3 pending
            elif order_trade.is_pending() and order_trade.is_pending_too_long():
                if order_trade.query_num < self.max_query_nums:
                    query_order = {"id": vt_orderid, "nums": order_trade.query_num}
                    query_orders.append(query_order)
                else:
                    recover_queue_content[vt_orderid] = "pending"
                    # self.add_to_recovery_queue(vt_orderid, "pending")
                continue

            # case4 status unknown
            elif order_trade.status == Status.UNKNOWN:
                if order_trade.query_num < self.max_query_nums:
                    query_order = {"id": vt_orderid, "nums": order_trade.query_num}
                    query_orders.append(query_order)
                else:
                    recover_queue_content[vt_orderid] = "unknown"

                continue

        if query_orders:
            selected_query_orders = select_orders_for_query_and_cancel(query_orders, self.max_batch_query_order_nums)
            self.batch_query(selected_query_orders)
            msg = f"[on_check_order_status: on_query] 本次需查询订单{query_orders}, 发送查询订单{selected_query_orders}"
            self.write_log(msg)

        # if cancel_orders:
        #     selected_cancel_orders = select_orders_for_query_and_cancel(cancel_orders, self.max_batch_cancel_order_nums)
        #     self.batch_cancel(selected_cancel_orders, checked_status=True)
        #     msg = f"[on_check_order_status: on_cancel] 本次需再次撤单{cancel_orders}, 发送再次撤单{selected_cancel_orders}"
        #     self.write_log(msg)

        if recover_queue_content:
            self.add_to_recovery_queue(recover_queue_content)

    def send_order(
            self,
            direction: Direction,
            price: float,
            volume: float,
            order_type: OrderType = OrderType.MAKER,
            ref: str = ""
    ) -> List:
        """"""
        # start_time = time.time_ns()
        contract: Optional[ContractData] = self.cta_engine.main_engine.get_contract(self.vt_symbol)
        # Round order price and volume to nearest incremental value
        price: float = round_to(price, contract.pricetick)
        volume: float = round_to(volume, contract.min_volume)
        # pos0_time = time.time_ns()
        if not contract:
            self.write_log(f"委托失败，找不到信息：{self.vt_symbol}")
            return []

        # Otherwise send order
        vt_orderids = self.cta_engine.send_order(
            self,
            contract,
            price,
            volume,
            direction,
            order_type,
            ref
        )
        # sended_time = time.time_ns()
        self.strategy_order_trades.extend(vt_orderids)

        # order_time = datetime.now()
        for vt_orderid in vt_orderids:

            if order_type in (OrderType.MAKER, OrderType.LIMIT):
                if direction == Direction.LONG:
                    self.buy_orderid_order_dict.update({vt_orderid: (price, volume, 1)})
                else:
                    self.sell_orderid_order_dict.update({vt_orderid: (price, volume, -1)})

        # updated_time = time.time_ns()
        msg = "SEND ORDER[{}]，{}，{}，{}, {}@{} ref={}".format(
            "|".join(vt_orderids),
            self.vt_symbol,
            direction.value,
            order_type.value,
            volume,
            price,
            ref
        )
        self.write_log(msg)
        # msg2 = (f"[on_template_send_order], "
        #         f"sended-start={sended_time - start_time},  "
        #         f"updated - sended={updated_time - sended_time}")
        # self.write_log(msg2)

        return vt_orderids

    def cancel_order(self, vt_orderid: str) -> None:
        """
        Cancel an existing order.
        """
        if self.trading:
            self.cta_engine.cancel_order(self, vt_orderid)

    def cancel_all(self) -> None:
        """
        Cancel all orders sent by strategy.
        """
        if self.trading:
            self.cta_engine.cancel_all(self)

    def cancel_all_orders(self) -> None:
        self.cta_engine.cancel_all_orders(self.vt_symbol)

    def clean_expired_order_trades(self, max_age_seconds):
        current_time_ns = time.time_ns()

        # 用列表存储需要删除的 key
        keys_to_remove = []

        # 遍历 order_trades 的键
        for key in self.order_trades.keys():
            order_trade = self.order_trades[key]
            if not order_trade.is_finished():
                continue

            # 提取 . 号后面的字符串，并转换为整数表示纳秒级时间戳
            timestamp_ns = int(key.split('.')[1])

            # 计算时间差
            time_difference_seconds = (current_time_ns - timestamp_ns) / 1e9

            # 如果时间差大于 max_age_seconds，记录该 key
            if time_difference_seconds > max_age_seconds:
                keys_to_remove.append(key)

        # 删除过期的键
        for key in keys_to_remove:
            del self.order_trades[key]

    def batch_cancel(self, vt_orderids: list, checked_status=False) -> None:
        cancel_orderids = []
        cancel_time = int(time.time() * 1e6)

        for vt_orderid in vt_orderids:
            order_trade = self.order_trades.get(vt_orderid)

            if not checked_status:
                "过滤检查"
                if order_trade is None:
                    continue

                if not order_trade.is_cancellable():
                    continue

            # 将订单 ID 添加到取消列表，并执行取消操作
            is_cancellable = order_trade.is_cancellable()
            cancel_orderids.append(vt_orderid)
            order_trade.cancel_num += 1
            order_trade.latest_cancel_time = cancel_time

            msg = (f"[on_batch_cancel]: {vt_orderid}, status: {order_trade.status}, "
                   f"exchange_status: {order_trade.exchange_status}, cancel_num: {order_trade.cancel_num}, "
                   f"is_cancellable: {is_cancellable},"
                   f"latest_cancel_time: {order_trade.latest_cancel_time}")

            self.write_log(msg)
            self.cancel_order(vt_orderid)

        if cancel_orderids:
            msg = "CANCEL ORDER[{}]".format(
                "|".join(cancel_orderids))
            self.write_log(msg)

    def batch_query(self, vt_orderids: list):
        query_time = int(time.time() * 1e6)

        for vt_orderid in vt_orderids:
            order_trade = self.order_trades.get(vt_orderid, None)
            query_request = order_trade.create_cancel_request()
            self.cta_engine.main_engine.query_order(query_request, gateway_name="BBC")
            order_trade.query_num += 1
            order_trade.latest_query_time = query_time

            msg = "[on_query]委托{}[{}]查询第{}次,最后交易所状态{}, 最后查询时间{}".format(
                order_trade.status.value,
                order_trade.vt_orderid,
                order_trade.query_num,
                order_trade.exchange_status.value,
                order_trade.latest_query_time
            )
            self.write_log(msg)

    def batch_insert(self, insert_orders, order_type=OrderType.MAKER, ref="") -> List:
        # print("enter batch insert")
        vt_orderids = []
        for insert_order in insert_orders:
            # start_time = time.time_ns()

            direction = Direction.LONG if insert_order[2] == 1 else Direction.SHORT
            # print("send order")
            [vt_orderid] = self.send_order(direction, insert_order[0], insert_order[1], order_type, ref=ref)
            vt_orderids.append(vt_orderid)

        return vt_orderids

    def batch_amend(self, vt_orderids: list) -> None:
        for vt_orderid in vt_orderids:
            self.cta_engine.amend_order(self, vt_orderid)
            # del self.active_orders[vt_orderid]

        msg = "AMEND ORDERS[{}]".format(
            "|".join(vt_orderids))
        self.write_log(msg)

    def is_single_order_finished(self, vt_orderid):
        if vt_orderid in self.strategy_order_trades:
            return False
        else:
            return True

    # def is_single_order_cancelable(self, vt_orderid, response_tolerant=30):
    #     order_trade = self.order_trades.get(vt_orderid, None)
    #     if not order_trade:
    #         msg = "[on_cancel_again]委托[{}]撤单失败, 请检查".format(vt_orderid)
    #         self.write_log(msg)
    #         self.send_dingding(msg)
    #         return False
    #
    #     if order_trade.is_cancellable(response_tolerant, self.max_query_nums):
    #         return True

    def write_log(self, msg: str) -> None:
        """
        Write a log message.
        """
        self.cta_engine.write_log(msg, self)

    def get_engine_type(self) -> EngineType:
        """
        Return whether the cta_engine is backtesting or live trading.
        """
        return self.cta_engine.get_engine_type()

    def get_pricetick(self) -> float:
        """
        Return pricetick data of trading contract.
        """
        return self.cta_engine.get_pricetick(self)

    def get_lot_size(self) -> float:
        return self.cta_engine.get_lot_size(self)

    def get_size(self) -> int:
        """
        Return size data of trading contract.
        """
        return self.cta_engine.get_size(self)

    def load_bar(
            self,
            days: int,
            interval: Interval = Interval.MINUTE,
            callback: Callable = None,
            use_database: bool = False
    ) -> None:
        """
        Load historical bar data for initializing strategy.
        """
        if not callback:
            callback: Callable = self.on_bar

        bars: List[BarData] = self.cta_engine.load_bar(
            self.vt_symbol,
            days,
            interval,
            callback,
            use_database
        )

        for bar in bars:
            callback(bar)

    def load_tick(self, days: int) -> None:
        """
        Load historical tick data for initializing strategy.
        """
        ticks: List[TickData] = self.cta_engine.load_tick(self.vt_symbol, days, self.on_tick)

        for tick in ticks:
            self.on_tick(tick)

    def put_event(self) -> None:
        """
        Put an strategy data event for ui update.
        """
        if self.inited:
            self.cta_engine.put_strategy_event(self)

    def send_email(self, msg) -> None:
        """
        Send email to default receiver.
        """
        if self.inited:
            self.cta_engine.send_email(msg, self)

    def sync_data(self) -> None:
        """
        Sync strategy variables value into disk storage.
        """
        if self.trading:
            self.cta_engine.sync_strategy_data(self)

    def on_abnormal(self, msg):
        self.write_log(f"交易异常，停止策略，请检查日志, msg={msg}")
        self.send_dingding(f'{self.strategy_name}: 交易异常, 停止策略, 请检查日志,msg={msg}')
        pid = os.getpid()
        os.kill(pid, signal.SIGTERM)

    def send_dingding(self, msg):
        self.cta_engine.main_engine.send_dingding(msg)


class CtaSignal(ABC):
    """"""

    def __init__(self) -> None:
        """"""
        self.signal_pos = 0

    @virtual
    def on_tick(self, tick: TickData) -> None:
        """
        Callback of new tick data update.
        """
        pass

    @virtual
    def on_bar(self, bar: BarData) -> None:
        """
        Callback of new bar data update.
        """
        pass

    def set_signal_pos(self, pos) -> None:
        """"""
        self.signal_pos = pos

    def get_signal_pos(self) -> Any:
        """"""
        return self.signal_pos


class TargetPosTemplate(CtaTemplate):
    """"""
    tick_add = 1

    last_tick: TickData = None
    last_bar: BarData = None
    target_pos = 0

    def __init__(self, cta_engine, strategy_name, vt_symbol, setting) -> None:
        """"""
        super().__init__(cta_engine, strategy_name, vt_symbol, setting)

        self.active_orderids: list = []
        self.cancel_orderids: list = []

        self.variables.append("target_pos")

    @virtual
    def on_tick(self, tick: TickData) -> None:
        """
        Callback of new tick data update.
        """
        self.last_tick = tick

    @virtual
    def on_bar(self, bar: BarData) -> None:
        """
        Callback of new bar data update.
        """
        self.last_bar = bar

    @virtual
    def on_order(self, order: OrderData) -> None:
        """
        Callback of new order data update.
        """
        vt_orderid: str = order.vt_orderid

        if not order.is_active():
            if vt_orderid in self.active_orderids:
                self.active_orderids.remove(vt_orderid)

            if vt_orderid in self.cancel_orderids:
                self.cancel_orderids.remove(vt_orderid)

    def check_order_finished(self) -> bool:
        """"""
        if self.active_orderids:
            return False
        else:
            return True

    def set_target_pos(self, target_pos) -> None:
        """"""
        self.target_pos = target_pos
        self.trade()

    def trade(self) -> None:
        """"""
        if not self.check_order_finished():
            self.cancel_old_order()
        else:
            self.send_new_order()

    def cancel_old_order(self) -> None:
        """"""
        for vt_orderid in self.active_orderids:
            if vt_orderid not in self.cancel_orderids:
                self.cancel_order(vt_orderid)
                self.cancel_orderids.append(vt_orderid)

    def send_new_order(self) -> None:
        """"""
        pos_change = self.target_pos - self.pos
        if not pos_change:
            return

        long_price = 0
        short_price = 0

        if self.last_tick:
            if pos_change > 0:
                long_price = self.last_tick.ask_price_1 + self.tick_add
                if self.last_tick.limit_up:
                    long_price = min(long_price, self.last_tick.limit_up)
            else:
                short_price = self.last_tick.bid_price_1 - self.tick_add
                if self.last_tick.limit_down:
                    short_price = max(short_price, self.last_tick.limit_down)

        else:
            if pos_change > 0:
                long_price = self.last_bar.close_price + self.tick_add
            else:
                short_price = self.last_bar.close_price - self.tick_add

        if self.get_engine_type() == EngineType.BACKTESTING:
            if pos_change > 0:
                vt_orderids: list = self.buy(long_price, abs(pos_change))
            else:
                vt_orderids: list = self.short(short_price, abs(pos_change))
            self.active_orderids.extend(vt_orderids)

        else:
            if self.active_orderids:
                return

            if pos_change > 0:
                if self.pos < 0:
                    if pos_change < abs(self.pos):
                        vt_orderids: list = self.cover(long_price, pos_change)
                    else:
                        vt_orderids: list = self.cover(long_price, abs(self.pos))
                else:
                    vt_orderids: list = self.buy(long_price, abs(pos_change))
            else:
                if self.pos > 0:
                    if abs(pos_change) < self.pos:
                        vt_orderids: list = self.sell(short_price, abs(pos_change))
                    else:
                        vt_orderids: list = self.sell(short_price, abs(self.pos))
                else:
                    vt_orderids: list = self.short(short_price, abs(pos_change))
            self.active_orderids.extend(vt_orderids)
